﻿// ===============================================================================
// XCI.NET 西安交通信息WinForm开发框架 2018 版权所有
// ===============================================================================
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using XCI.Extensions;

namespace XCI.Helper
{
    /// <summary>
    /// 树结构操作帮助类
    /// </summary>
    public static class TreeHelper
    {
        public const string RootTreeNodeId = "0";
        public const int SortPathLength = 4;

        /// <summary>
        /// 树形查询条件
        /// </summary>
        /// <param name="entityList">数据源</param>
        /// <param name="condition">查询条件</param>
        /// <param name="primaryKeyName">主键字段</param>
        /// <param name="parentIdName">上级主键字段</param>
        /// <returns></returns>
        public static List<T> TreeWhere<T>(this List<T> entityList, Func<T, bool> condition,
            string primaryKeyName = "Id", string parentIdName = "ParentId") where T : class
        {
            var locateList = entityList.Where(condition);
            var parameter = Expression.Parameter(typeof(T), "t");
            //模糊查询表达式
            List<T> treeList = new List<T>();
            foreach (T entity in locateList)
            {
                //先把自己加入进来
                treeList.Add(entity);
                //向上查询
                string pId = entity.GetType().GetProperty(parentIdName).GetValue(entity, null).ToString();
                while (true)
                {
                    if (string.IsNullOrEmpty(pId) && pId == RootTreeNodeId)
                    {
                        break;
                    }
                    var upLambda = (Expression.Equal(parameter.Property(primaryKeyName), Expression.Constant(pId))).ToLambda<Func<T, bool>>(parameter).Compile();
                    T upRecord = entityList.FirstOrDefault(upLambda);
                    if (upRecord != null)
                    {
                        treeList.Add(upRecord);
                        pId = upRecord.GetType().GetProperty(parentIdName).GetValue(upRecord, null).ToString();
                    }
                    else
                    {
                        break;
                    }
                }
            }
            return treeList.Distinct().ToList();
        }
        /// <summary>
        /// 树形查询条件
        /// </summary>
        public static DataTable TreeWhere(this DataTable table, string condition,
            string primaryKeyName = "Id", string parentIdName = "ParentId")
        {
            DataRow[] drs = table.Select(condition);
            DataTable treeTable = table.Clone();
            foreach (DataRow dr in drs)
            {
                treeTable.ImportRow(dr);
                string pId = dr[parentIdName].ToString();
                while (true)
                {
                    if (string.IsNullOrEmpty(pId) && pId == RootTreeNodeId)
                    {
                        break;
                    }
                    DataRow[] pdrs = table.Select(primaryKeyName + "='" + pId + "'");
                    if (pdrs.Length > 0)
                    {
                        treeTable.ImportRow(pdrs[0]);
                        pId = pdrs[0][parentIdName].ToString();
                    }
                    else
                    {
                        break;
                    }
                }
            }
            DataView treeView = treeTable.DefaultView;
            return treeView.ToTable(true);
        }

        /// <summary>
        /// 获取所有子节点
        /// </summary>
        public static List<T> GetChildren<T>(List<T> entityList, T entity, bool hasSelf = false,
            string primaryKeyName = "Id", string parentIdName = "ParentId")
        {
            var storeList = new List<T>();
            if (hasSelf)
            {
                storeList.Add(entity);
            }
            GetChild(entityList, storeList, entity, primaryKeyName, parentIdName);
            return storeList;
        }

        private static void GetChild<T>(List<T> entityList, List<T> storeList, T entity,
            string primaryKeyName = "Id", string parentIdName = "ParentId")
        {
            string id = ObjectHelper.GetObjectValue(entity, primaryKeyName).ToStringOrEmpty();
            var data = entityList.Where(p => ObjectHelper.GetObjectValue(p, parentIdName).ToStringOrEmpty().Equals(id)).ToList();
            if (data.Count <= 0) return;
            foreach (var item in data)
            {
                storeList.Add(item);
                GetChild(entityList, storeList, item, primaryKeyName, parentIdName);
            }
        }

        /// <summary>
        /// 创建Tree排序路径
        /// </summary>
        /// <param name="parentSortPath">父级排序路径</param>
        /// <param name="index">序号</param>
        /// <returns>返回新创建的排序路径</returns>
        public static string GetSortPathCore(string parentSortPath, int index)
        {
            if (parentSortPath == null)
            {
                parentSortPath = string.Empty;
            }
            return parentSortPath + StringHelper.GetFixedLengthString(index.ToString(), SortPathLength, "0", true);
        }

        /// <summary>
        /// 更新子项的排序路径
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <returns>返回修改的实体字典,key为主键</returns>
        public static Dictionary<string, T> UpdateSortPath<T>(List<T> all, string parendId,
            string primaryKeyName = "Id", string parentIdName = "ParentId", string sortPathName = "SortPath")
        {
            var store = new Dictionary<string, T>();
            if (parendId.IsEmpty() || parendId.Equals(RootTreeNodeId))
            {
                var roots = all.Where(p => ObjectHelper.GetObjectValue(p, parentIdName).ToStringOrEmpty().Equals(RootTreeNodeId));
                foreach (var item in roots)
                {
                    UpdateSortPathCore(all, store, item, primaryKeyName, parentIdName, sortPathName);
                }
            }
            else
            {
                var parent = all.FirstOrDefault(p => ObjectHelper.GetObjectValue(p, primaryKeyName).ToStringOrEmpty().Equals(parendId));
                UpdateSortPathCore(all, store, parent, primaryKeyName, parentIdName, sortPathName);
            }
            return store;
        }

        private static void UpdateSortPathCore<T>(List<T> all, Dictionary<string, T> store,
            T parent, string primaryKeyName = "Id", string parentIdName = "ParentId", string sortPathName = "SortPath")
        {
            string sortPath = ObjectHelper.GetObjectValue(parent, sortPathName).ToStringOrEmpty();
            if (sortPath.IsEmpty()) return;
            string id = ObjectHelper.GetObjectValue(parent, primaryKeyName).ToStringOrEmpty();
            if (id.IsEmpty()) return;
            var children = all.Where(p => ObjectHelper.GetObjectValue(p, parentIdName).ToStringOrEmpty().Equals(id)).ToList();
            if (children.Count == 0) return;
            children.OrderByAsc(sortPathName);
            for (int i = 0; i < children.Count; i++)
            {
                var item = children[i];
                var itemId = ObjectHelper.GetObjectValue(item, primaryKeyName).ToStringOrEmpty();
                var newSortPath = GetSortPathCore(sortPath, (i + 1));
                if (!newSortPath.Equals(ObjectHelper.GetObjectValue(item, sortPathName).ToStringOrEmpty()))
                {
                    ObjectHelper.SetObjectValue(item, sortPathName, newSortPath);
                    store[itemId] = item;
                }
                UpdateSortPathCore(all, store, item, primaryKeyName, parentIdName, sortPathName);
            }
        }
    }
}